package com.chinackts.auth.service;

import com.alibaba.fastjson.JSONObject;
import com.chinackts.auth.model.MenuType;
import com.chinackts.auth.repository.redis.MenuTypeRepository;
import com.chinackts.common.exception.ApplicationException;
import com.chinackts.common.okhttp.OkHttpRequest;
import com.chinackts.common.utils.AES;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.HashMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @author Lianhong
 * @since 2021/10/26
 * 解密js
 * 抓取的不同接口的响应数据对应不同的解密JS，通过解密JS中的key以及iv进项AES解密
 */
@Service
public class MenuTypeService {

    private static final Logger LOGGER = LoggerFactory.getLogger(MenuTypeService.class);

    private static final HashMap<MenuType, String> MENUTYPE_URL_MAP = new HashMap<MenuType, String>() {{
        put(MenuType.Rtx, "https://i.chinaport.gov.cn/rtxwebserver/menuType");      //报关单查询解密js
        put(MenuType.Fec, "https://i.chinaport.gov.cn/fecwebserver/menuType");      //收付汇查询解密js
    }};

    @Resource
    private MenuTypeRepository menuTypeRepository;
    @Resource
    private CookieStoreService cookieStoreService;
    @Resource(name = "proxyOkHttpRequest")
    private OkHttpRequest request;

    /**
     * 报关单查询解密js
     */
    public void menuTypeRequestAndStore(String id, MenuType type) {
        boolean exists = menuTypeRepository.exists(id, type);
        if (exists) return;
        String url = MENUTYPE_URL_MAP.get(type);
        String result = request.get(url, null);
        if (StringUtils.isEmpty(result)) result = request.get(url, null);
        menuTypeRepository.saveMenuType(id, this.menuTypeSplit(result), type);
    }

    /**
     * AES解密
     */
    public String decode(String id, String response, MenuType type) {
        String menuTypeStr = this.menuTypeRepository.getMenuType(id, type);
        if (StringUtils.isEmpty(menuTypeStr)) {
            this.menuTypeRequestAndStore(id, type);
            menuTypeStr = this.menuTypeRepository.getMenuType(id, type);
        }
        return this.decode(id, response, menuTypeStr);
    }

    /**
     * 解密响应数据
     *
     * @param response 响应数据
     */
    private String decode(String id, String response, String menuTypeStr) {
        if (StringUtils.isEmpty(menuTypeStr)) {
            throw new ApplicationException("请重新初始化登录");
        }
        JSONObject keyAndIv = parseKeyAndIvFromMenuType(menuTypeStr);
        String key = keyAndIv.getString("key");
        String iv = keyAndIv.getString("iv");
        if (StringUtils.isBlank(key) || StringUtils.isBlank(iv)) {
            LOGGER.error("响应数据解析异常, 获取key或者iv失败");
            throw new ApplicationException("响应数据解析异常");
        }
        response = AES.desEncrypt(response, key, iv);
        if (StringUtils.isBlank(response)) return StringUtils.EMPTY;
        for (String sessionId : cookieStoreService.getJSESSIONID(id)) {
            response = response.replaceAll(sessionId, "");
        }
        return response;
    }

    /**
     * 获取 DES解密 所需的 key || iv
     *
     * @return json {key: '', iv: ''}
     */
    private JSONObject parseKeyAndIvFromMenuType(String menuTypeStr) {
        Pattern keyPattern = Pattern.compile("varkey=CryptoJS.enc.Latin1.parse\\(\'(.*?)\'\\);");
        Matcher keyMatcher = keyPattern.matcher(menuTypeStr);
        JSONObject result = new JSONObject();
        String key = StringUtils.EMPTY;
        if (keyMatcher.find()) {
            key = keyMatcher.group(1);
        }
        Pattern ivPattern = Pattern.compile("variv=CryptoJS.enc.Latin1.parse\\(\'(.*?)\'\\);");
        Matcher ivMatcher = ivPattern.matcher(menuTypeStr);
        String iv = StringUtils.EMPTY;
        if (ivMatcher.find()) {
            iv = ivMatcher.group(1);
        }
        result.put("key", key);
        result.put("iv", iv);
        return result;
    }

    /**
     * 截取menuType（取带有 key、iv 的js代码片段）
     */
    private String menuTypeSplit(String menuType) {
        if (StringUtils.isEmpty(menuType)) return StringUtils.EMPTY;
        String[] menuTypeArr = menuType.split("function loadMenuTypeStr");
        if (menuTypeArr.length < 2) return StringUtils.EMPTY;
        return menuTypeArr[1].replaceAll(" ", "");
    }
}